package com.yunxitest.yunxitest.item.audio.`in`

import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder
import android.os.Handler
import android.os.HandlerThread
import android.os.Message
import com.yunxitest.yunxitest.Interface.IResultCallback
import com.yunxitest.yunxitest.utils.L
import com.yunxitest.yunxitest.utils.SystemPropertiesProxy
import java.nio.ByteBuffer
import java.util.concurrent.LinkedBlockingQueue


class HardwareAudioSource(val queue: LinkedBlockingQueue<ByteBuffer>,inResultCallback : IResultCallback?) {
    private val TAG = "testAudio"
    private  var mInResultCallback : IResultCallback ?= null
    private val AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT
    private val AUDIO_CHINNELS = AudioFormat.CHANNEL_IN_MONO
    private val AUDIO_SAMPLE_RATE = 44100
    private val AUDIO_SAMPLE_PERFRAME = 1024
    private var AUDIO_SOURCE = MediaRecorder.AudioSource.MIC

    private val SOURCE_RECORDER_LINE = -1
    private val SOURCE_RECORDER_HDMI1 = 0
    private val SOURCE_RECORDER_HDMI2 = 1
    private val SOURCE_USB = 2

    private var sampleRate: Int = AUDIO_SAMPLE_RATE

    private var audioRecord: AudioRecord? = null
    var audioRecordThread: AudioRecordThread?
    private var isSampleRateInvalid = false

    val lineAudioSource = HardwareAudioSource(SOURCE_RECORDER_LINE)

    // todo remove
    //   var saveHelper = AudioSaveHelper(AUDIO_SAMPLE_RATE)

    init {
        audioRecordThread = AudioRecordThread()
        if (!audioRecordThread!!.isAlive) {
            audioRecordThread?.priority = 9
            audioRecordThread?.start()
        }
        //mInResultCallback = inResultCallback
    }

    fun release() {
        audioRecordThread?.sendQuitMesssage()
        audioRecordThread = null

    }

    inner class AudioRecordThread : HandlerThread("AudioRecordThread") {
        private val MSG_STATUS_RUNNING = 1
        private val MSG_STATUS_STOP = 0
        private val MSG_STATUS_START = 2
        private val MSG_STATUS_QUIT = 3

        private var handler: Handler? = null
        private var buf: ByteBuffer
        private var recordBufSize: Int = 0
        private var readingFlag = false

        init {
            recordBufSize = AudioRecord.getMinBufferSize(AUDIO_SAMPLE_RATE, AUDIO_CHINNELS, AUDIO_ENCODING)
            audioRecord = AudioRecord(AUDIO_SOURCE, AUDIO_SAMPLE_RATE, AUDIO_CHINNELS, AUDIO_ENCODING, recordBufSize)
            buf = ByteBuffer.allocateDirect(AUDIO_SAMPLE_PERFRAME * 2)
            isDaemon = true
        }

        override fun onLooperPrepared() {
            super.onLooperPrepared()
            handler = object : Handler(looper) {
                override fun handleMessage(msg: Message?) {
                    super.handleMessage(msg)
                    val what = msg?.what
                    when (what) {
                        MSG_STATUS_START -> {
                            startRecord(msg.obj as Int)
                            sendRunningMesssage()
                            L.d(TAG, "MSG_STATUS_START : startRecord")
                        }

                        MSG_STATUS_RUNNING -> {
                            readingFlag = true

                            readAudioDataPersistence()
                            L.dd(TAG, "MSG_STATUS_RUNNING   ### limit  ${buf.limit()}", 100)
                        }

                        MSG_STATUS_STOP -> {
                            readingFlag = false
                            stopRecord()
                            removeMessages(MSG_STATUS_RUNNING)
                            L.d(TAG, "MSG_STATUS_STOP : stopRecord")
                        }

                        MSG_STATUS_QUIT -> {
                            readingFlag = false
                            removeCallbacksAndMessages(null)
                            quitSafely()
                            L.d(TAG, "MSG_STATUS_QUIT ")
                        }
                    }
                }
            }
        }

        fun startRecord(source: Int) {
            recordBufSize = AudioRecord.getMinBufferSize(sampleRate, AUDIO_CHINNELS, AUDIO_ENCODING)
            audioRecord = AudioRecord(AUDIO_SOURCE, sampleRate, AUDIO_CHINNELS, AUDIO_ENCODING, recordBufSize)
            try {
                audioRecord?.startRecording()
            } catch (e: IllegalStateException) {
                L.d(TAG, "IllegalStateException : " + e.toString())
                e.printStackTrace()
                sendStopMessage()
                mInResultCallback?.fail(e.toString())
            }
            // todo remove
            //  saveHelper = AudioSaveHelper(AUDIO_SAMPLE_RATE, AUDIO_CHINNELS, ENCODING_PCM_16BIT )
            //  saveHelper?.createNewFile()
        }

        fun stopRecord() {
            try {
                audioRecord?.stop()
            } catch (e: IllegalStateException) {
                L.d(TAG, "IllegalStateException : " + e.toString())
                e.printStackTrace()
            }
            audioRecord?.release()
            audioRecord = null
            //todo remove
            //   saveHelper?.stop()
        }

        fun sendStartMessage(source: Int) {
            switchSource(source)
            handler?.sendMessageDelayed(handler?.obtainMessage(MSG_STATUS_START, source), 200)
        }

        fun sendStopMessage() {
            readingFlag = false
            handler?.sendMessage(handler?.obtainMessage(MSG_STATUS_STOP))
        }

        fun sendRunningMesssage() {
            handler?.sendMessage(handler?.obtainMessage(MSG_STATUS_RUNNING))
        }

        fun sendQuitMesssage() {
            readingFlag = false
            handler?.sendMessage(handler?.obtainMessage(MSG_STATUS_QUIT))
        }

        private fun readAudioDataPersistence() {
            var i=0
            var sum=0
            while (readingFlag && !isSampleRateInvalid) {
                try {
                    val buf = ByteBuffer.allocateDirect(recordBufSize)
                    val len = audioRecord?.read(buf, buf.capacity()) ?: return
                    L.d("testAudio"," read # $len")
                    if (len >= 0 && len <= buf.capacity()) {
                        buf.position(len)
                        buf.flip()
                        queue.offer(buf)
                        var dio=calculateDecibel(buf.array())
                        sum+=dio
                        i++
                        if(mInResultCallback !=null&&i>=30){
                            sum=sum/i
                            if(sum>=7) {
                                mInResultCallback?.success()
                                mInResultCallback = null
                            }else{
                                mInResultCallback?.fail("声音分贝太小")
                                mInResultCallback = null
                            }
                        }
                    }
//                //todo remove
                    //  val data = ByteArray(len)
                    // buf.get(data)
                    //  saveHelper?.writeData(data)
                } catch (e: IllegalStateException) {
                    L.d(TAG, "IllegalStateException : " + e.toString())
                    e.printStackTrace()
                    mInResultCallback?.fail(e.toString())
                }
            }
        }
    }

    fun calculateDecibel(buf: ByteArray): Int {
        var sum = 0
        for (i in 0 until buf.size) {
            sum += Math.abs(buf[i].toInt())
        }
        val size = if (buf.isNotEmpty()) buf.size else 1
        // avg 10-50
        return sum / size
    }


    private fun switchSource(source: Int) {
        SystemPropertiesProxy["persist.hdmi.camrecord"] = "false"
        SystemPropertiesProxy["persist.usb.camrecord"] = "false"     // 切换到系统mic
        L.d(TAG, "switch to mic  line ")
    }

    private fun startRecorder(source: Int) {
        audioRecordThread?.sendStopMessage()
        audioRecordThread?.sendStartMessage(source)
    }

    private fun stopRecorder() {
        audioRecordThread?.sendStopMessage()
    }

    inner class HardwareAudioSource(val sourceId: Int) : BaseAudioSource(sourceId, null) {

        override fun startRecord() {
            status = STATUS_RUNNING
            startRecorder(sourceId)
        }

        override fun stopRecord() {
            status = STATUS_STOP
            stopRecorder()
        }

        override fun close() {
            status = STATUS_STOP
            stopRecorder()
        }
    }

}